module ProxyPump
  class Sources
    include Enumerable

    class Error < StandardError    ; end
    class InvalidNodeError < Error ; end
    class NodeMissingError < Error ; end

    class NodeSelector
      ACCEPTABLE_TYPES = %i(xpath cass).freeze

      def self.acceptable_type?(type)
        ACCEPTABLE_TYPES.include? type.to_sym
      end

      attr_reader :name, :selector, :type

      def initialize(name, selector, type = :xpath)
        @name     = name
        @selector = selector
        @type     = type.to_sym
      end

      def to_h
        { name: name, selector: selector, type: type }
      end

    end # NodeSelector

    class ProxySelector
      attr_reader :entry, :host, :port, :anon, :protocal

      def initialize(options)
        assign(options)
      end

      private

        def assign(options)
          %i(entry host port anon protocal).each do |name|
            raise NodeMissingError, "Missing #{name} node." unless options.key?(name)
            node_attrs = options[name]
            raise InvalidNodeError, 'Node should specify a selector and type of selector.' unless valid_node?(node_attrs)
            instance_variable_set :"@#{name}", NodeSelector.new(name, node_attrs[:selector], node_attrs[:type])
          end
        end

        def valid_node?(node_attrs)
          !node_attrs[:selector].nil? && NodeSelector.acceptable_type?(node_attrs[:type])
        end

    end # ProxySelector

    class Source
      attr_reader :name, :url, :proxy_selector

      def initialize(name, url, proxy_selector)
        @name = name
        @url  = url
        @proxy_selector = proxy_selector
      end

      %w(entry host port anon protocal).each do |name|
        class_eval <<-RUBY, __FILE__, __LINE__ + 1
          def #{name}_selector
            proxy_selector.#{name}.selector
          end

          def #{name}_selector_type
            proxy_selector.#{name}.type
          end
        RUBY
      end

    end # Source

    def initialize(yml)
      load yml
    end

    def all
      @all ||= @data[:sources].map do |name, item|
        urls = item[:urls]
        proxy_selector = ProxySelector.new(item)
        urls.map { |url| Source.new(name, url, proxy_selector) }
      end.flatten
    end

    def site_names
      @data[:sources].keys
    end

    def each(&block)
      all.each(&block)
    end

    def load(yml)
      self.tap { @data = YAML.load(File.read(yml)).deep_symbolize_keys! ; @all = nil }
    end

  end # Sources
end # ProxyPump
